﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Milo.Framework
{
	/// <summary>
	/// Defines a control which is a container for a list of other controls
	/// </summary>
	/// <typeparam name="TModel">The type of the model.</typeparam>
	public class ControlList<TModel, TModelControl> : ModelControl<IList<TModel>>, IControlList<TModel, TModelControl>
		where TModelControl : class, IModelControl<TModel>, new()
	{
		private const string arrayPattern = "[{0}]";

		#region constructors
		public ControlList() { }
		public ControlList(IList<TModel> models) { Value = models; }
		public ControlList(string name, IList<TModel> models) : this(models) { Name = name; }
		#endregion

		#region private fields
		private List<TModelControl> controls;
		#endregion

		#region IList<TModelControl> Members

		public int IndexOf(TModelControl item)
		{
			throw new NotImplementedException();
			//return controls.IndexOf(item);
		}

		public void Insert(int index, TModelControl item)
		{
			throw new NotSupportedException();
		}

		public void RemoveAt(int index)
		{
			throw new NotSupportedException();
		}

		public TModelControl this[int index]
		{
			get
			{
				Ensure();

				TModelControl item;

				//cos there's no proper resize
				while (controls.Count <= index)
					controls.Add(null);

				item = controls[index];
				if (item == null)
					controls[index] = item = NewItem(index);

				return item;
			}
			set
			{
				throw new NotSupportedException();
			}
		}

		#endregion

		#region ICollection<TModelControl> Members

		public void Add(TModelControl item)
		{
			Ensure();
			controls.Add(item);
		}

		public void Clear()
		{
			Ensure();
			controls.Clear();
		}

		public bool Contains(TModelControl item)
		{
			Ensure();

			return controls.Contains(item);
		}

		public void CopyTo(TModelControl[] array, int arrayIndex)
		{
			Ensure();

			controls.CopyTo(array, arrayIndex);
		}

		/// <summary>
		/// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// <para>Note. This is either the underlying values count, or the the controls if there are no values</para>
		/// </summary>
		/// <value></value>
		/// <returns>The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</returns>
		public int Count
		{
			get
			{
				Ensure();

				if (Value == null)
					return controls.Count;
				else
					return Value.Count;
			}
		}

		public bool IsReadOnly
		{
			get { return true; }
		}

		public bool Remove(TModelControl item)
		{
			throw new NotSupportedException("Can't remove a control as this would affect the underlying list");
		}

		#endregion

		#region IEnumerable<TModelControl> Members

		public IEnumerator<TModelControl> GetEnumerator()
		{
			Ensure();

			return new ControlEnumerator(this);
		}

		#endregion

		#region IEnumerable Members

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			Ensure();

			return new ControlEnumerator(this);
		}

		#endregion

		public override IList<TModel> Value
		{
			get
			{
				return base.Value;
			}
			set
			{
				base.Value = value;
				this.controls = null;
			}
		}

		public override Type GetModelType()
		{
			return typeof(TModel);
		}

		public override string GetModelIDPrefix()
		{
			if (string.IsNullOrEmpty(Name))
				return string.Empty;
			else
				return ModelID;
		}

		/// <summary>
		/// Ensures the controls array is there
		/// </summary>
		public void Ensure()
		{

			if (controls == null)
				this.controls = new List<TModelControl>();
		}


		private TModelControl NewItem(int index)
		{
			TModelControl item = new TModelControl();
			item.LinkToParent(string.Format(arrayPattern, index), this, GetValue, SetValue, index);
			controls[index] = item;
			return item;
		}

		/// <summary>
		/// Gets the array element model value.
		/// </summary>
		/// <param name="index">The index.</param>
		/// <returns></returns>
		private TModel GetValue(int index) { return Value[index]; }
		/// <summary>
		/// Sets the array element model value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <param name="index">The index.</param>
		private void SetValue(TModel value, int index) { Value[index] = value; }

		public class ControlEnumerator : IEnumerator<TModelControl>
		{
			private ControlList<TModel, TModelControl> list;
			private int index;
			private int max;

			public ControlEnumerator(ControlList<TModel, TModelControl> list)
			{
				this.index = -1;
				this.list = list;
				this.max = list.Count() - 1;
			}

			#region IEnumerator<TModelControl> Members

			public TModelControl Current
			{
				get { return list[index]; }
			}

			#endregion

			#region IDisposable Members

			public void Dispose()
			{
				//throw new NotImplementedException();
			}

			#endregion

			#region IEnumerator Members

			object System.Collections.IEnumerator.Current
			{
				get { return list[index]; }
			}

			public bool MoveNext()
			{
				if (index < max)
				{
					index++;
					return true;
				}
				else
					return false;
			}

			public void Reset()
			{
				index = -1;
			}

			#endregion
		}
	}
}
